﻿#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "qdatetime.h"
#include "usrt_rev.h"
#include "interface_switch.h"
#include <QVBoxLayout>
#include <QIODevice>
#include <QTimer>
#include <QMessageBox>
#include <windows.h>
#include <io.h>  // 包含 _get_osfhandle 的定义
static Ui::MainWindow *ui_main = nullptr;
static QTimer *timer_uart_status = nullptr; // 定义静态的timer_main指针
static QTimer *timer_67_fault = nullptr; // 定义静态的timer_main指针
static QTimer *timer_start_test = nullptr;//开始测试定时器
static timer_info timer_data;
static QString TEST_ITEM[8] = {"0","5","6","7","8","9","10","12"};
void timer_info_init(void)
{
    timer_data.time_outflag = 0;
}
void set_timer_outflag(uint8_t time_outflag)
{
    timer_data.time_outflag = time_outflag;
}
uint8_t rt_timer_outflag(void)
{
    return timer_data.time_outflag;
}

// 构造函数：MainWindow
// 参数：parent - 指向父窗口小部件的指针
// 说明：MainWindow类的构造函数，初始化主窗口及其组件
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    // 初始化用户界面组件
    ui->setupUi(this);

    // 禁用radioButton控件，防止用户在未准备就绪时进行选择
    ui->radioButton->setEnabled(false);

    // 模拟点击pushButton_5，触发相应操作
    //on_pushButton_5_clicked();

    // 设置comboBox_2的当前索引为1，即选择第二个选项
    ui->comboBox_2->setCurrentIndex(1);
    ui_main = ui;
    // 连接serial对象的readyRead信号到MainWindow的data_receive槽
    // 用于在接收到数据时处理数据接收
    connect(serial, &QSerialPort::readyRead, this, &MainWindow::data_receive);

    timer_u_s = new QTimer(this);
    timer_67_f = new QTimer(this);
    timer_start_t = new QTimer(this);
    timer_uart_status = timer_u_s;
    timer_67_fault = timer_67_f;
    timer_start_test = timer_start_t;
    connect(timer_u_s, &QTimer::timeout, this, &MainWindow::timer_out_uart_status_fun);
    connect(timer_67_f, &QTimer::timeout, this, &MainWindow::timer_out_67_fault_fun);
    connect(timer_start_t, &QTimer::timeout, this, &MainWindow::timer_out_start_test_fun);
    timer_info_init();
    set_pushButton_6_Enabled(false);
    ui->pushButton->setEnabled(false);
}

/**
 * 控制定时器的启动与停止
 *
 * @param st 决定定时器是启动还是停止的标志，非零值表示启动，零表示停止
 * @param var 定时器启动时的参数，表示定时器的某种设置值或间隔
 */
void time_ruart_status_start_stop(uint8_t st, uint32_t var)
{
    // 检查timer_main对象是否有效
    if (timer_uart_status)
    {
        // 根据st的值决定定时器的操作
        if (st)
        {
            // 启动定时器，并传递设置值
            timer_uart_status->stop();
            timer_uart_status->start(var);
        }
        else
        {
            // 停止定时器
            timer_uart_status->stop();
        }
    }
}

void time_67_fault_start_stop(uint8_t st, uint32_t var)
{
    // 检查timer_main对象是否有效
    if (timer_67_fault)
    {
        // 根据st的值决定定时器的操作
        if (st)
        {
            // 启动定时器，并传递设置值
            timer_67_fault->stop();
            timer_67_fault->start(var);
        }
        else
        {
            // 停止定时器
            timer_67_fault->stop();
        }
    }
}

void timer_start_test_start_stop(uint8_t st, uint32_t var)
{
    // 检查timer_main对象是否有效
    if (timer_start_test)
    {
        // 根据st的值决定定时器的操作
        if (st)
        {
            // 启动定时器，并传递设置值
            timer_start_test->stop();
            timer_start_test->start(var);
        }
        else
        {
            // 停止定时器
            timer_start_test->stop();
        }
    }
}

/**
 * 设置UI表格的行列数和显示属性
 * 此函数统一设置QTableWidget的列数和行数，并调整其显示属性以适应特定的UI需求
 *
 * @param x 表格的列数
 * @param y 表格的行数
 */
void set_ui_QTable_xy(uint8_t x, uint8_t y)
{
    // 设置表格的列数
    ui_main->tableWidget->setColumnCount(x);
    // 设置表格的行数
    ui_main->tableWidget->setRowCount(y);
    // 隐藏水平表头
    ui_main->tableWidget->horizontalHeader()->close();
    // 隐藏垂直表头
    ui_main->tableWidget->verticalHeader()->close();
    // 设置水平表头的单元格调整方式为充满整个表格宽度
    ui_main->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    // 设置垂直表头的单元格调整方式为充满整个表格高度
    ui_main->tableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
}

/**
 * 重置表格控件的显示内容。
 *
 * 本函数旨在清除表格控件上的所有数据、列和行，将其恢复到初始空白状态。
 * 这在需要重新加载或初始化界面时非常有用，确保用户看到的是一个干净、没有之前数据残留的表格。
 */
void set_ui_QTable_close(void)
{
    // 清除表格控件上的所有数据
    ui_main->tableWidget->clear();
    // 设置列数为0，即清除所有列
    ui_main->tableWidget->setColumnCount(0);
    // 设置行数为0，即清除所有行
    ui_main->tableWidget->setRowCount(0);
}

/**
 * 设置QTableWidget单元格内容的函数
 * 此函数用于在图形界面的表格中设置特定单元格的内容，并确保其不可编辑
 *
 * @param x 表格中的列索引
 * @param y 表格中的行索引
 * @param text 要设置的单元格文本内容，通过引用传递以避免复制开销
 */
void set_ui_QTable_content(uint8_t x, uint8_t y, QString text)
{
    // 新建一个项，内容为传入的text
    QTableWidgetItem *itemStatus = new QTableWidgetItem(text);
    // 设置新建项的属性：可选中但不可编辑。通过按位与操作去除可编辑标志
    itemStatus->setFlags(itemStatus->flags() & (~Qt::ItemIsEditable));
    //itemStatus->setFlags(itemStatus->flags() | (Qt::ItemIsEditable));
    // 更新到列表指定的行和列，以y作为行索引，x作为列索引
    ui_main->tableWidget->setItem(y, x, itemStatus);
}


void set_ui_QTable_content_iseditable(uint8_t x, uint8_t y ,bool iseditable)
{
    QTableWidgetItem *item =ui_main->tableWidget->item(y,x);
    if(item)
    {
        if(iseditable)
        {
            item->setFlags(item->flags() | Qt::ItemIsEditable);/*可修改*/
        }else{
            item->setFlags(item->flags() & (~Qt::ItemIsEditable));/*不可修改*/
        }
    }
}

uint32_t rt_QTable_value(uint8_t x, uint8_t y)
{
    uint32_t value = 0;
    QTableWidgetItem *itemStatus = ui_main->tableWidget->item(y,x);
    if(itemStatus){
        QString text = itemStatus->text();
        value = text.toUInt();
    }
    return value;
}

void MainWindow::timer_out_uart_status_fun(void)
{
    time_ruart_status_start_stop(0, 0); // 停止定时器
    set_timer_outflag(1);   // 设置超时标志
    timerout_interface();
}

void MainWindow::timer_out_67_fault_fun(void)
{
    /*开启新的30s定时器*/
    time_67_fault_start_stop(1,30*1000);
    Item_67_fault_time_fun();
}

void MainWindow::timer_out_start_test_fun(void)
{
    //qDebug() << "timeout_1s";
    timer_start_test_interface();
}

// MainWindow类的析构函数
MainWindow::~MainWindow()
{
    // 关闭串口连接
    serial->close();
    // 释放用户界面资源
    delete ui;
}

/**
 * @brief 当pushButton_5被点击时执行的槽函数
 *
 * 该函数负责更新UI上的comboBox，以显示当前可用的串口列表
 * 它首先清空comboBox，然后遍历系统中的所有可用串口，
 * 并将它们的名称添加到comboBox中
 */
void MainWindow::on_pushButton_5_clicked()
{
    // 清空comboBox，准备重新填充串口列表
    ui->comboBox->clear();

    // 遍历系统中所有可用的串口信息
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        // 检查临时串口的名称是否与当前使用的串口名称相同，或者尝试打开串口进行读写
        //if (_com.portName() == serial->portName() or _com.open(QIODevice::ReadWrite))
        // 如果条件满足，将串口名称添加到comboBox中
        ui->comboBox->addItem(info.portName());
    }
}

void set_comboBox_Enabled(bool st)
{
    ui_main->comboBox->setEnabled(st);
    ui_main->comboBox_2->setEnabled(st);
    ui_main->comboBox_3->setEnabled(st);
    ui_main->comboBox_4->setEnabled(st);
    ui_main->comboBox_5->setEnabled(st);
    ui_main->comboBox_6->setEnabled(st);
    ui_main->pushButton_5->setEnabled(st);
}

/// 处理推按钮点击事件
void MainWindow::on_pushButton_clicked()
{
    // 检查无线电按钮是否未选中
    if (!ui->radioButton->isChecked())
    {
         // 获取当前日期和时间
        QString currentDate = QDate::currentDate().toString("yyyy-MM-dd");  // 格式化日期
        QString currentTime = QTime::currentTime().toString("HH时mm分ss秒");  // 格式化时间
        // 配置串口设置
        Serial_Set();
        // 尝试以读写模式打开串口，如果失败则返回
        if (!serial->open(QSerialPort::ReadWrite))
        {
            QMessageBox::critical(this, "错误", QString("无法打开串口 %1").arg(serial->portName()));
            return;
        }

        // 如果串口成功打开，选中无线电按钮并更改推按钮文本
        ui->radioButton->setChecked(true);
        ui->pushButton->setText("关闭串口");
        set_comboBox_Enabled(false);
        on_pushButton_set_ui_QTable(false);
        get_QTable_value();

    // 构造文件名
    QString fileName = QString("#%1_log_%2_%3(文件创建时间).txt")
                        .arg(TEST_ITEM[T_item])           // 插入测试项目
                           .arg(currentDate)       // 插入日期
                           .arg(currentTime);       // 插入时间

        
        file.setFileName(fileName);
        qDebug() << fileName;
        if (!file.open(QIODevice::ReadWrite))
        {
            qDebug() << "Failed to open file for reading";
            return;
        }

    }
    else
    {
        // 如果无线电按钮已选中，关闭串口并更改推按钮文本
        serial->close();
        ui->radioButton->setChecked(false);
        ui->pushButton->setText("打开串口");
        file.flush();
        file.close();
        set_comboBox_Enabled(true);
        on_pushButton_set_ui_QTable(true);
    }
}

// 配置串口参数
void MainWindow::Serial_Set()
{
    // 串口名
    serial->setPortName(ui->comboBox->currentText());
    // 波特率
    serial->setBaudRate((ui->comboBox_2->currentText()).toLong());
    // 停止位
    if (ui->comboBox_3->currentText() == "1")
        serial->setStopBits(QSerialPort::OneStop);
    else if (ui->comboBox_3->currentText() == "1.5")
        serial->setStopBits(QSerialPort::OneAndHalfStop);
    else if (ui->comboBox_3->currentText() == "2")
        serial->setStopBits(QSerialPort::TwoStop);
    // 数据位
    if (ui->comboBox_4->currentText() == "8")
        serial->setDataBits(QSerialPort::Data8);
    else if (ui->comboBox_4->currentText() == "7")
        serial->setDataBits(QSerialPort::Data7);
    else if (ui->comboBox_4->currentText() == "6")
        serial->setDataBits(QSerialPort::Data7);
    else if (ui->comboBox_4->currentText() == "5")
        serial->setDataBits(QSerialPort::Data7);
    // 校验位
    if (ui->comboBox_5->currentText() == "None")
        serial->setParity(QSerialPort::NoParity);
    else if (ui->comboBox_5->currentText() == "Even")
        serial->setParity(QSerialPort::EvenParity);
    else if (ui->comboBox_5->currentText() == "Odd")
        serial->setParity(QSerialPort::OddParity);

    // 流控设置为无流控
    serial->setFlowControl(QSerialPort::NoFlowControl);
    // 设置读缓冲区大小为1024字节
    serial->setReadBufferSize(1024);
}

/**
 * 当pushButton_2被点击时清除textBrowser中的内容
 */
void MainWindow::on_pushButton_2_clicked()
{
    ui->textBrowser->clear();
}

// 处理数据接收的函数
void MainWindow::data_receive()
{
    // 将接收到的数据追加到缓冲区
    QByteArray data = serial->readAll();
    uart_buffer.append(data);

    // 初始化一个空字符串，用于存储转换后的数据

    QByteArray message;

    // 将缓冲区中的数据转换为字符串并追加到之前的字符串中
    // 数据是一行一行传送的，要保存所有数据
    QString string = "";
    string += tr(data);

    /*在数据前插入时间戳：[时:分:秒:毫秒]:RX -> 数据*/
    string = QString("[%1 %2]:RX -> %3")
                 .arg(QDate::currentDate().toString("MM-dd-")) // 只包含月份和日期
                 .arg(QTime::currentTime().toString("HH:mm:ss:zzz")) // 时间戳
                 .arg(string); // 原始数据

    // 将接收到的数据插入到textBrowser控件的末尾
    ui->textBrowser->append(string);
    file.write(string.toLocal8Bit());
    file.write("\r\n");
    file.flush();
    HANDLE hFile = (HANDLE)_get_osfhandle(file.handle());
    FlushFileBuffers(hFile);
    // 将光标移动到textBrowser控件的最后一行，以便用户始终看到最新的数据
    ui->textBrowser->moveCursor(QTextCursor::End);

    // 检查缓冲区中是否有完整的报文
    while (!uart_buffer.isEmpty())
    {
        int endPos = uart_buffer.indexOf("\n"); // 假设报文以换行符结束
        if (endPos != -1)
        {
            // 提取完整的报文
            message = uart_buffer.mid(0, endPos + 1);
            uart_buffer = uart_buffer.mid(endPos + 1); // 移除已处理的报文

            data_handle(message);
        }
        else
        {
            // 缓冲区中没有完整的报文，退出循环
            break;
        }
    }
}

/**
 * @brief 处理测试项组合框激活事件的槽函数
 *
 * 该函数在组合框comboBox_6中的某个选项被选中时调用。它首先删除当前界面，然后根据用户选择的索引设置测试项目，
 * 最后重新添加新的界面。这个过程用于更新界面以反映用户的选择。
 *
 * @param index 组合框中被选中项的索引，用于确定当前选择的测试项目
 */
void MainWindow::on_comboBox_6_activated(int index)
{
    ui->pushButton->setEnabled(true);
    // 删除当前界面，准备根据用户选择更新界面
    delt_interface();

    // 根据组合框的选中项索引设置当前测试项目
    T_item = (Test_item)index;

    // 重新添加界面，以反映测试项目的变更
    add_interface();
}

void set_pushButton_6_Enabled(bool st)
{
    ui_main->pushButton_6->setText("开始测试");
    ui_main->pushButton_6->setEnabled(st);
    if (false == st)
    {
        timer_start_test_start_stop(0,0);
    }

}

/// 处理测试开始和停止的槽函数
void MainWindow::on_pushButton_6_clicked()
{
    // 判断按钮文本以确定当前操作是开始测试还是停止测试
    if (ui->pushButton_6->text() == "开始测试") {
        // 当按钮文本为"开始测试"时，更改按钮文本为"停止测试"
        ui->pushButton_6->setText("停止测试");
        // 设置测试时间显示为初始状态
        set_ui_QTable_content(1, 0, "00:00:00");
        // 初始化测试数据
        timer_start_test_data_init();
        // 启动定时器，参数分别为使能状态和定时器周期
        timer_start_test_start_stop(1,1000);
    }else if (ui->pushButton_6->text() == "停止测试")
    {
        // 当按钮文本为"停止测试"时，更改按钮文本为"开始测试"
        ui->pushButton_6->setText("开始测试");
        // 停止定时器
        timer_start_test_start_stop(0,0);
    }

}

